package com.cloudcentral.config;

import com.cloudcentral.filter.SMSAuthenticationFilter;
import com.cloudcentral.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.authentication.*;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

/**
 * Create by Administrator
 * 2023/11/10 11:10
 */
public class SMSLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H, SMSLoginConfigurer<H>, SMSAuthenticationFilter> {
  private final SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
  private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
  private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
  private final UserService userDetailService;
  private final RedissonClient redissonClient;

  public SMSLoginConfigurer(String loginProcessingUrl, UserService userDetailService, RedissonClient redissonClient) {
    super(new SMSAuthenticationFilter(AntPathRequestMatcher.antMatcher(HttpMethod.POST, loginProcessingUrl)), loginProcessingUrl);
    this.userDetailService = userDetailService;
    this.redissonClient = redissonClient;
  }

  @Override
  public void configure(H http) throws Exception {
    PortMapper portMapper = http.getSharedObject(PortMapper.class);
    if (portMapper != null) this.authenticationEntryPoint.setPortMapper(portMapper);
    RequestCache requestCache = http.getSharedObject(RequestCache.class);
    if (requestCache != null) this.defaultSuccessHandler.setRequestCache(requestCache);
    // 添加认证提供者
    http.authenticationProvider(new SMSAuthenticationProvider(userDetailService, redissonClient));
    // 设置过滤器
    SMSAuthenticationFilter smsAuthenticationFilter = this.getAuthenticationFilter();
    smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    if (this.authenticationDetailsSource != null) smsAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);

    SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);
    if (sessionAuthenticationStrategy != null) smsAuthenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);

    RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
    if (rememberMeServices != null) smsAuthenticationFilter.setRememberMeServices(rememberMeServices);

    SecurityContextConfigurer<H> securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
    if (securityContextConfigurer != null) {
      SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
      if (securityContextRepository == null) {
        securityContextRepository = new DelegatingSecurityContextRepository(
            new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());
      }
      smsAuthenticationFilter.setSecurityContextRepository(securityContextRepository);
    }
    smsAuthenticationFilter.setSecurityContextHolderStrategy(this.getSecurityContextHolderStrategy());
    SMSAuthenticationFilter filter = this.postProcess(smsAuthenticationFilter);
    http.addFilterAfter(filter, UsernamePasswordAuthenticationFilter.class);
  }

  @Override
  protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
    RequestMatcher matcher = AntPathRequestMatcher.antMatcher(HttpMethod.POST, loginProcessingUrl);
    this.getAuthenticationFilter().setMatcher(matcher);
    return matcher;
  }

  public SMSLoginConfigurer<H> setSuccessHandler(AuthenticationSuccessHandler successHandler) {
    this.getAuthenticationFilter().setAuthenticationSuccessHandler(successHandler);
    return super.successHandler(successHandler);
  }

  public SMSLoginConfigurer<H> setFailureHandler(AuthenticationFailureHandler failureHandler) {
    this.getAuthenticationFilter().setAuthenticationFailureHandler(failureHandler);
    return super.failureHandler(failureHandler);
  }

  public SMSLoginConfigurer<H> loginPage(String loginPage) {
    return super.loginPage(loginPage);
  }

  public SMSLoginConfigurer<H> phoneParameter(String phoneParameter) {
    (this.getAuthenticationFilter()).setPhoneParameter(phoneParameter);
    return this;
  }

  public SMSLoginConfigurer<H> codeParameter(String codeParameter) {
    this.getAuthenticationFilter().setCodeParameter(codeParameter);
    return this;
  }

  public SMSLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
    this.failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
    return this;
  }

  public SMSLoginConfigurer<H> successForwardUrl(String forwardUrl) {
    this.successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
    return this;
  }

  public void init(H http) throws Exception {
    super.init(http);
  }
}
